/*
 * hipnet-shm-test.c - a simulated HipNet SHM node
 * compile:  gcc hipnet-shm-test.c -o hipnet-shm-test 
 */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "hipnet.h"


// number of xlrs used
#define XLR_FIRST   0       
#define XLR_LAST    3       // 4 xlrs used

#define HIPNET_KEY 44779

// shm format for the IPC used to talk with a controller
typedef struct shm_block_t {
  uint32_t   seq;  // incremented for every operation, indicates a change                       
  uint8_t ack;    // used to ack reception of the frame
  hipnet_data_t   data; // hipnet data frame
} shm_block_t;

shm_block_t *tx_shm[XLR_LAST+1]; 
shm_block_t *rx_shm[XLR_LAST+1]; 

int tx_shmid[XLR_LAST+1];
key_t  tx_shmkey[XLR_LAST+1];

int rx_shmid[XLR_LAST+1];
key_t  rx_shmkey[XLR_LAST+1];





// ============================================
// ============================================
// =========== Shared Memory ==================
// ============================================
// ============================================

// create HipNet shared memory segments
// return > 0 on success, or various negative values when fails
int shm_start (void) {

 int cnt;

 //  --------  TX
 //  tx segments are data sent to XLR's
 // create tx shmem segments for every xlr
 for (cnt = XLR_FIRST ; cnt < XLR_LAST+1; cnt++) { 

    tx_shmkey[cnt] = HIPNET_KEY + cnt;
    
    // create the segment.
    if ((tx_shmid[cnt] = shmget(tx_shmkey[cnt], sizeof(shm_block_t), IPC_CREAT | 0666)) < 0) {
        perror("shmget");
        return 0;
    }

    // attach to the segment
    if ( (tx_shm[cnt] = shmat(tx_shmid[cnt], NULL, 0)) == (void *) -1 ) {
        perror("shmat");
        return 0;
    }
      
  // node does the initializing of the segment
  memset(tx_shm[cnt], 0, sizeof(shm_block_t));
  tx_shm[cnt]->data.head = htonl((uint32_t) HIPNET_DMX);
  tx_shm[cnt]->seq = 0;
  
 } // for each tx shmem segment xlr

//  --------  RX
//  rx segments contain dmx data received from XLR's
 // create rx shmem segments for every xlr
 for (cnt = XLR_FIRST ; cnt < XLR_LAST+1; cnt++) { 

    rx_shmkey[cnt] = HIPNET_KEY + (XLR_LAST+1) + cnt;

    // create the segment.
    if ((rx_shmid[cnt] = shmget(rx_shmkey[cnt], sizeof(shm_block_t), IPC_CREAT | 0666)) < 0) {
        perror("shmget");
        return 0;
    }

    // attach to the segment
    if ( (rx_shm[cnt] = shmat(rx_shmid[cnt], NULL, 0)) == (void *) -1 ) {
        perror("shmat");
        return 0;
    }

  // node does the initializing of the segment
  memset(rx_shm[cnt], 0, sizeof(shm_block_t));
  rx_shm[cnt]->data.head = htonl((uint32_t) HIPNET_DMX);
  rx_shm[cnt]->seq = 0;
  
 } // for each rx shmem segment xlr
  
  
return 1;    
}


// tx segments contain data set out of XLR's
// expects shm_start() has been called
int shm_tx_run_test (void) {
    static int last_seq[XLR_LAST+1];

    int cnt, cnt2;
    // for every xlr
    for (cnt = XLR_FIRST ; cnt < XLR_LAST+1; cnt++) { 
        // check for a new frame and display it
        if (tx_shm[cnt]->seq != last_seq[cnt]) {
          while (tx_shm[cnt]->ack) {
            // wait for ack to be 1
            // TODO: timeout
            usleep(1000);
           }
          last_seq[cnt] = tx_shm[cnt]->seq; 

         printf("%d:- univ: %d  seq: %d  mode: %d  ",cnt, tx_shm[cnt]->data.universe, tx_shm[cnt]->seq, tx_shm[cnt]->data.mode);
         for (cnt2 = 0 ; cnt2 < 513 ; cnt2++) {
          printf("%d=%d ",cnt2,tx_shm[cnt]->data.data[cnt2]);
         }
          printf("\n");
          tx_shm[cnt]->ack = 0;  // ack the reception data, ready for next
          return 1;
        }           
    }
 return 0;
}

//  rx segments are data recieved by XLR's 
// expects shm_start() has been called
int shm_rx_run_test (void) {
    static int last_seq[XLR_LAST+1];
    static unsigned short cntr[XLR_LAST+1];
    
    int cnt;
    
    // for every xlr
    for (cnt = XLR_FIRST ; cnt < XLR_LAST+1; cnt++) { 
        // we simulate DMX input here, from node to controller 
         rx_shm[cnt]->seq += 1;     
        //  printf("(%d): wait for ack...",cnt);
          while (rx_shm[cnt]->ack) {
            // wait for ack to be 0
            // TODO: timeout maybe
            usleep(500);
           }
          printf(".");
          
          last_seq[cnt] = rx_shm[cnt]->seq; 
            
          rx_shm[cnt]->data.mode=0x01;
          rx_shm[cnt]->data.universe = cnt;
          rx_shm[cnt]->data.srcnode = 0x00;  // controller
          rx_shm[cnt]->data.size = htons(512);  // controller       
          rx_shm[cnt]->data.data[0] = 0;
          rx_shm[cnt]->data.data[1] = 255;
          rx_shm[cnt]->data.data[2] = 127;
          rx_shm[cnt]->data.data[3] = 64;
          rx_shm[cnt]->data.data[4] = cnt;
          rx_shm[cnt]->data.data[5] = ++cntr[cnt];
          rx_shm[cnt]->data.data[510] = 64;
          rx_shm[cnt]->data.data[511] = 127;
          rx_shm[cnt]->data.data[512] = 255;       
          
          rx_shm[cnt]->ack = 1;  // flag new data for consumer        
//          printf("sent!\n");
          
      }
      return 1;
}



int main(void)
{
  
   if (shm_start()){
   
   for (;;){
  //   shm_rx_run_test();
     shm_tx_run_test();
     usleep(22727); // 44+/- fps
   }
  }
  return 0;
}

